package com.adwyxx.reflection;

import java.lang.reflect.Constructor;
import java.lang.reflect.Field;
import java.lang.reflect.Method;

/**
 * 反射的应用示例
 * <pre>
 *  // 1. 获取 Class 对象
 *  Class clazz = Class.forName(className);
 *
 *  // 2. 获取对应类的构造函数
 *  Constructor constructor = clazz.getDeclaredConstructor();
 *  constructor.setAccessible(true); // 设置为可访问，以便访问私有构造函数
 *
 *  // 3. 动态的创建对应类的对象
 *  T instance = (T) constructor.newInstance();
 *
 *  // 4. 根据方法名获取类的成员方法
 *  Method method = clazz.getDeclaredMethod(methodName);
 *  method.setAccessible(true); // 设置为可访问，以便访问私有方法
 *  return method.invoke(instance); // 调用方法，并返回方法的返回值
 *
 *  // 5.获取属性对象，例如：id属性
 *  Field idField = clazz.getDeclaredField("id");
 *  idField.setAccessible(true); // 设置为可访问，以便访问私有属性
 *  Object value = idField.get(stu); // 获取属性值
 *  idField.set(stu, 2);    // 设置属性值
 * </pre>
 *
 * @author Leo.W
 * @date 2024-09-09 15:50
 */
public class ReflectionTest {

    class Person {
        private String name;

        public Person() {
        }

        public Person(String name) {
            this.name = name;
        }

        public String getName() {
            return name;
        }

        public void setName(String name) {
            this.name = name;
        }

        public void sayHello() {
            System.out.println("Hello, " + name);
        }
    }

    public static void main(String[] args) throws Exception {

        /*---------- 1.获取Class对象 ----------*/
        // Class<?> clazz = Class.forName("com.adwyxx.reflection.ReflectionTest");
        // 反射获取内部类的Class对象：需要通过外部类的Class对象的 getDeclaredClasses() 方法获取
        Class<?> clazz = ReflectionTest.class.getDeclaredClasses()[0];

        System.out.println(clazz);
        /*---------- 2.创建对象 ----------*/
        // 方式一：使用构造函数创建
        // 方式一：使用 Class.newInstance() 方法创建
        // 需要先创建外部类的实例，然后使用这个实例来创建内部类的实例
        Object outerInstance = ReflectionTest.class.newInstance();
        // 获取内部累的构造函数：第一个参数需要传入内部类对应的外部类的Class对象
        Constructor constructor = clazz.getConstructor(ReflectionTest.class, String.class);
        Person person1 = (Person) constructor.newInstance(outerInstance, "李四");
        person1.sayHello();

        // 获取无参的构造函数并创建对象
        // 需要先创建外部类的实例，然后使用这个实例来创建内部类的实例
        Person person = (Person) clazz.getConstructor(ReflectionTest.class).newInstance(outerInstance);

        /*---------- 3.访问对象的成员属性 ----------*/
        // 获取属性
        Field field = clazz.getDeclaredField("name");
        // 设置属性可访问：因为是私有的，所以需要设置为可访问
        field.setAccessible(true);
        // 设置属性值
        field.set(person, "张三");
        // 获取属性值
        String name = (String) field.get(person);
        System.out.println("姓名：" + name);

        /*---------- 4.调用对象的方法 ----------*/
        // 获取方法
        Method sayHMethod = clazz.getMethod("sayHello");
        // 调用方法
        sayHMethod.invoke(person);
    }
}
